#include "byte_stream.hh"

#include <iostream>
// Dummy implementation of a flow-controlled in-memory byte stream.

// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.

// You will need to add private members to the class declaration in
// `byte_stream.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}

using namespace std;

ByteStream::ByteStream(const size_t capacity)
    : buffer(std::deque<char>()), _error(false), cap(capacity), wlen(0), rlen(0), write_end(false) {}

size_t ByteStream::write(const string &data) {
    size_t remain = cap - buffer.size();
    size_t canwrite = min(remain, data.length());
    for (size_t i = 0; i < canwrite; i++) {
        buffer.push_back(data[i]);
    }
    wlen += canwrite;
    return canwrite;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
    string out = "";
    size_t remain = buffer.size();
    size_t cancp = min(len, remain);
    for (size_t i = 0; i < cancp; i++) {
        out += buffer[i];
    }
    return out;
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
    if (len > buffer.size()) {
        set_error();
        return;
    }
    for (size_t i = 0; i < len; i++) {
        buffer.pop_front();
    }
    rlen += len;
    return;
}

//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len) {
    string out = "";
    if (len > buffer.size()) {
        set_error();
        return out;
    }
    for (size_t i = 0; i < len; i++) {
        out += buffer[0];
        buffer.pop_front();
    }
    rlen += len;
    return out;
}

void ByteStream::end_input() { write_end = true; }

bool ByteStream::input_ended() const { return write_end; }

size_t ByteStream::buffer_size() const { return buffer.size(); }

bool ByteStream::buffer_empty() const { return buffer.empty(); }

bool ByteStream::eof() const { return buffer.empty() && write_end; }

size_t ByteStream::bytes_written() const { return wlen; }

size_t ByteStream::bytes_read() const { return rlen; }

size_t ByteStream::remaining_capacity() const { return cap - buffer.size(); }
